package util;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.openssl.PasswordFinder;
import org.bouncycastle.util.encoders.Hex;

/**
 * Reads keys out of files.
 * 
 * @author Lukas
 *
 */
public class KeyReader {
	private HashMap<String, PublicKey> pubKeyMap = new HashMap<String, PublicKey>();
	private PrivateKey priKey;
	private HashMap<String, SecretKey> secKeyMap = new HashMap<String, SecretKey>();
	private String pubKeyPath, priKeyPath, secKeyPath;
	private static KeyReader keyReader = new KeyReader();
	private Object priKeyLock = new Object(); //just for synchronization
	
	private KeyReader() {}
	
	/**
	 * Get the singleton object.
	 * @return The singleton object.
	 */
	public static KeyReader getInstance() {
		return keyReader;
	}
	
//	/**
//	 * Reads the public key from a given file.
//	 * @return The key.
//	 * @throws IOException
//	 */
//	public PublicKey readPublicKey() throws IOException {
//		return readPublicKey(user);
//	}
	
	/**
	 * Reads the public key from a given file.
	 * @param username The username.
	 * @return The key.
	 * @throws IOException
	 */
	public PublicKey readPublicKey(String username) throws IOException {
		String path = pubKeyPath.replaceAll("<user>", username);
		PublicKey pubKey;
		synchronized (pubKeyMap) {
			pubKey = pubKeyMap.get(path);
			if (pubKey == null) {
				PEMReader in = new PEMReader(new FileReader(path)); 
				pubKey = (PublicKey) in.readObject();
				pubKeyMap.put(path, pubKey);
			}
		}
		return pubKey;
	}
	
	/**
	 * Reads the private key from a given file.
	 * @param useStored <tt>true</tt>, if the saved password shall be used.
	 * @return The key.
	 * @throws IOException
	 */
	public PrivateKey readPrivateKey(boolean useStored, String user) throws IOException {
		String path = priKeyPath.replaceAll("<user>", user);
		synchronized (priKeyLock) {
			if (priKey == null || !useStored) {
				PEMReader in = new PEMReader(new FileReader(path), new PasswordFinder() {
					@Override
					public char[] getPassword() {
						// reads the password from standard input for decrypting the private key
						System.out.println("Enter pass phrase:");
						try {
							String s = new BufferedReader(new InputStreamReader(System.in)).readLine();
							return s.toCharArray();
						} catch (IOException e) {
							return new char[0];
						}
					}
				});
				KeyPair keyPair = (KeyPair) in.readObject();
				priKey = keyPair.getPrivate();
			}
		}
		return priKey;
	}
	
	/**
	 * Reads the secret key from a file.
	 * @return The key.
	 * @throws IOException
	 */
	public SecretKey readSecretKey(String user) throws IOException {
		String path = secKeyPath.replaceAll("<user>", user);
		SecretKey sk = secKeyMap.get(path);
		if (sk == null) {
			byte[] keyBytes = new byte[1024];
			FileInputStream fis = new FileInputStream(path);
			fis.read(keyBytes);
			fis.close();
			byte[] input = Hex.decode(keyBytes);
			sk = new SecretKeySpec(input,"HMAC");
			secKeyMap.put(path, sk);
		}
		return sk;
	}
	
//	/**
//	 * Replaces the <tt>{@literal <}user></tt>-tag in the paths with the given name.
//	 * @param name The user name.
//	 */
//	public void setUserName(String name) {
//		user = name;
//	}
	
	/**
	 * Setter for the path to the public key file.
	 * All <tt>{@literal <}user></tt>-tags  can be replaced with {@link #setUserName(String)}.
	 * @param path The path to the public key file.
	 */
	public void setPubKeyPath(String path) {
		pubKeyPath = path;
	}
	
	/**
	 * Setter for the path to the private key file.
	 * All <tt>{@literal <}user></tt>-tags  can be replaced with {@link #setUserName(String)}.
	 * @param path The path to the private key file.
	 */
	public void setPriKeyPath(String path) {
		priKeyPath = path;
	}
	
	/**
	 * Setter for the path to the secret key file.
	 * All <tt>{@literal <}user></tt>-tags  can be replaced with {@link #setUserName(String)}.
	 * @param path The path to the secret key file.
	 */
	public void setSecKeyPath(String path) {
		secKeyPath = path;
	}

//	/**
//	 * @return the username
//	 */
//	public String getUserName() {
//		return user;
//	}
}
